Multi-category dot-density maps often work well when the categories cluster geographically. Visible minority data work well for this. ## Data First we grab the immigrant data via cancensus, making use of the CensusMapper API tool to select the regions and variables we need.

#devtools::install_github("mountainmath/cancensus")
library(cancensus)
library(dotdensity)
library(cancensusHelpers)
# options(cancensus.api_key='your_api_key')
regions=list(CMA="59933")
vectors=search_census_vectors("Total - Visible minority for the population in private households", "CA16","Total") %>% child_census_vectors(TRUE) %>% pull("vector")

We choose the categories and colours we want to map and define a convenience function to rename the variables and compute the qantities for the other asian countries that we don’t break out.

categories=c("South Asian", "Chinese", "Black", "Filipino", "Latin American", "Arab", "Southeast Asian", "West Asian", "Korean", "Japanese", "Visible minority, n.i.e.", "Multiple visible minorities", "Not a visible minority")
colors=c("#33a02c", "#e31a1c", "#b15928", "#1f78b4", "#fdbf6f", "#ff7f00", "#fb9a99", "#b2df8a", "#cab2d6", "#6a3d9a", "#a6cee3", "#ffff99", "#e0e0e0")
prep_data <- function(geo){
  data <- geo@data %>% replace(is.na(.), 0)
  geo@data <- data
  geo <- detail_labels(geo)
  return(geo)
}

Next we grab the data via cancensus,

data_csd=get_census(dataset = 'CA16', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='CSD') %>% prep_data
OGR data source with driver: GeoJSON 
Source: "/Users/jens/.cancensus_cache/CM_geo_6b42dc9bcf266cb7c9bfbbbf40889c49.geojson", layer: "OGRGeoJSON"
with 39 features
It has 11 fields
data_ct=get_census(dataset = 'CA16', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='CT') %>% prep_data
OGR data source with driver: GeoJSON 
Source: "/Users/jens/.cancensus_cache/CM_geo_4a5626a6774fefc22c656038b44803a8.geojson", layer: "OGRGeoJSON"
with 478 features
It has 11 fields
data_da=get_census(dataset = 'CA16', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='DA') %>% prep_data
OGR data source with driver: GeoJSON 
Source: "/Users/jens/.cancensus_cache/CM_geo_e0f185a0c335b032df17fa063ccc9238.geojson", layer: "OGRGeoJSON"
with 3450 features
It has 10 fields
data_db=get_census(dataset = 'CA16', regions=regions,geo_format='sp',labels='short',level='DB')
OGR data source with driver: GeoJSON 
Source: "/Users/jens/.cancensus_cache/CM_geo_ebd06436dd29d8db88d687516f5a8c04.geojson", layer: "OGRGeoJSON"
with 15197 features
It has 10 fields

which we then re-aggregate to make sure we don’t miss overall counts due to privacy cutoffs distribute them proportionally among the population.

data_ct@data <- dot_density.proportional_re_aggregate(data=data_ct@data,parent_data=data_csd@data,geo_match=setNames("GeoUID","CSD_UID"),categories=categories,base="Population")
data_da@data <- dot_density.proportional_re_aggregate(data=data_da@data,parent_data=data_ct@data,geo_match=setNames("GeoUID","CT_UID"),categories=categories,base="Population")
data_db@data <- dot_density.proportional_re_aggregate(data=data_db@data,parent_data=data_da@data,geo_match=setNames("GeoUID","DA_UID"),categories=categories,base="Population")

Map

All that’s left to do is to covert our re-aggregated block-level data to dots, using the dot_density.compute_dots function from the dotdensity package and feed it into the dot_density.dots_map function to add them to our basemap.

# 1 dot = 5 immigrants
scale=25
tight_x <- c(-123.28857421875,-122.96310424804688)
tight_y <- c(49.16329776043147,49.347493514019895)
wide_x <-c(-123.29,-122.6)
wide_y <- c(49.02,49.35)
dots.db <- dot_density.compute_dots(geo_data = data_db, categories = categories, scale=scale)
basemap +
  # zoom in a bit
  coord_fixed(xlim=wide_x, ylim=wide_y, ratio = 1/cos(49.2/180*pi)) +
# shade unpopulated blocks
#  geom_polygon(data=data_db[data_db$Population<=5,], 
#                        aes(long, lat, group = group), 
#                        fill = "#222222", size=0.1, 
#                        color = "#222222") +
  scale_colour_manual(values = colors) +
  labs(color = "",
                title="Visible Minority 2016",
                caption="Source: StatCan Census 2016 via cancensus & CensusMapper.ca",
                subtitle = paste0("1 dot = ",scale," people")) + 
  dot_density.dots_map(dots=dots.db,alpha=0.75,size=0.25)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

# save image for later
ggsave('../images/minority.png',width=26,height=26)
scale=10
dots.db2 <- dot_density.compute_dots(geo_data = data_db, categories = categories, scale=scale)
basemap +
  # zoom in a bit
  coord_fixed(xlim=tight_x, ylim=tight_y, ratio = 1/cos(49.2/180*pi)) +
# shade unpopulated blocks
#  geom_polygon(data=data_db[data_db$Population<=5,], 
#                        aes(long, lat, group = group), 
#                        fill = "#222222", size=0.1, 
#                        color = "#222222") +
  scale_colour_manual(values = colors) +
  labs(color = "",
                title="Visible Minority 2016",
                caption="Source: StatCan Census 2016 via cancensus & CensusMapper.ca",
                subtitle = paste0("1 dot = ",scale," people")) + 
  dot_density.dots_map(dots=dots.db2,alpha=0.75,size=0.25)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# save image for later
ggsave('../images/minority_van.png',width=26,height=26)

LS0tCnRpdGxlOiAiVmlzaWJsZSBNaW5vcml0eSIKYXV0aG9yOiAiSmVucyB2b24gQmVyZ21hbm4iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiBodG1sX25vdGVib29rCnZpZ25ldHRlOiA+CiAgJVxWaWduZXR0ZUluZGV4RW50cnl7VmlzaWJsZSBNaW5vcml0eX0KICAlXFZpZ25ldHRlRW5naW5le2tuaXRyOjpybWFya2Rvd259CiAgJVxWaWduZXR0ZUVuY29kaW5ne1VURi04fQotLS0KCk11bHRpLWNhdGVnb3J5IGRvdC1kZW5zaXR5IG1hcHMgb2Z0ZW4gd29yayB3ZWxsIHdoZW4gdGhlIGNhdGVnb3JpZXMgY2x1c3RlciBnZW9ncmFwaGljYWxseS4gVmlzaWJsZSBtaW5vcml0eSBkYXRhIHdvcmsgd2VsbCBmb3IgdGhpcy4KIyMgRGF0YQpGaXJzdCB3ZSBncmFiIHRoZSBpbW1pZ3JhbnQgZGF0YSB2aWEgW2NhbmNlbnN1c10oaHR0cHM6Ly9naXRodWIuY29tL21vdW50YWluTWF0aC9jYW5jZW5zdXMpLCBtYWtpbmcgdXNlIG9mIHRoZSBbQ2Vuc3VzTWFwcGVyIEFQSSB0b29sXShodHRwczovL2NlbnN1c21hcHBlci5jYS9hcGkvQ0ExMSkgdG8gc2VsZWN0IHRoZSByZWdpb25zIGFuZCB2YXJpYWJsZXMgd2UgbmVlZC4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIm1vdW50YWlubWF0aC9jYW5jZW5zdXMiKQpsaWJyYXJ5KGNhbmNlbnN1cykKbGlicmFyeShkb3RkZW5zaXR5KQpsaWJyYXJ5KGNhbmNlbnN1c0hlbHBlcnMpCiMgb3B0aW9ucyhjYW5jZW5zdXMuYXBpX2tleT0neW91cl9hcGlfa2V5JykKcmVnaW9ucz1saXN0KENNQT0iNTk5MzMiKQp2ZWN0b3JzPXNlYXJjaF9jZW5zdXNfdmVjdG9ycygiVG90YWwgLSBWaXNpYmxlIG1pbm9yaXR5IGZvciB0aGUgcG9wdWxhdGlvbiBpbiBwcml2YXRlIGhvdXNlaG9sZHMiLCAiQ0ExNiIsIlRvdGFsIikgJT4lIGNoaWxkX2NlbnN1c192ZWN0b3JzKFRSVUUpICU+JSBwdWxsKCJ2ZWN0b3IiKQpgYGAKCldlIGNob29zZSB0aGUgY2F0ZWdvcmllcyBhbmQgY29sb3VycyB3ZSB3YW50IHRvIG1hcCBhbmQgZGVmaW5lIGEgY29udmVuaWVuY2UgZnVuY3Rpb24gdG8gcmVuYW1lIHRoZSB2YXJpYWJsZXMgYW5kIGNvbXB1dGUgdGhlIHFhbnRpdGllcyBmb3IgdGhlIG90aGVyIGFzaWFuIGNvdW50cmllcyB0aGF0IHdlIGRvbid0IGJyZWFrIG91dC4KYGBge3J9CmNhdGVnb3JpZXM9YygiU291dGggQXNpYW4iLCAiQ2hpbmVzZSIsICJCbGFjayIsICJGaWxpcGlubyIsICJMYXRpbiBBbWVyaWNhbiIsICJBcmFiIiwgIlNvdXRoZWFzdCBBc2lhbiIsICJXZXN0IEFzaWFuIiwgIktvcmVhbiIsICJKYXBhbmVzZSIsICJWaXNpYmxlIG1pbm9yaXR5LCBuLmkuZS4iLCAiTXVsdGlwbGUgdmlzaWJsZSBtaW5vcml0aWVzIiwgIk5vdCBhIHZpc2libGUgbWlub3JpdHkiKQpjb2xvcnM9YygiIzMzYTAyYyIsICIjZTMxYTFjIiwgIiNiMTU5MjgiLCAiIzFmNzhiNCIsICIjZmRiZjZmIiwgIiNmZjdmMDAiLCAiI2ZiOWE5OSIsICIjYjJkZjhhIiwgIiNjYWIyZDYiLCAiIzZhM2Q5YSIsICIjYTZjZWUzIiwgIiNmZmZmOTkiLCAiI2UwZTBlMCIpCgpwcmVwX2RhdGEgPC0gZnVuY3Rpb24oZ2VvKXsKICBkYXRhIDwtIGdlb0BkYXRhICU+JSByZXBsYWNlKGlzLm5hKC4pLCAwKQogIGdlb0BkYXRhIDwtIGRhdGEKICBnZW8gPC0gZGV0YWlsX2xhYmVscyhnZW8pCiAgcmV0dXJuKGdlbykKfQpgYGAKCgpOZXh0IHdlIGdyYWIgdGhlIGRhdGEgdmlhIGBjYW5jZW5zdXNgLApgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhX2NzZD1nZXRfY2Vuc3VzKGRhdGFzZXQgPSAnQ0ExNicsIHJlZ2lvbnM9cmVnaW9ucyx2ZWN0b3JzPXZlY3RvcnMsZ2VvX2Zvcm1hdD0nc3AnLGxhYmVscz0nc2hvcnQnLGxldmVsPSdDU0QnKSAlPiUgcHJlcF9kYXRhCmRhdGFfY3Q9Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLCByZWdpb25zPXJlZ2lvbnMsdmVjdG9ycz12ZWN0b3JzLGdlb19mb3JtYXQ9J3NwJyxsYWJlbHM9J3Nob3J0JyxsZXZlbD0nQ1QnKSAlPiUgcHJlcF9kYXRhCmRhdGFfZGE9Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLCByZWdpb25zPXJlZ2lvbnMsdmVjdG9ycz12ZWN0b3JzLGdlb19mb3JtYXQ9J3NwJyxsYWJlbHM9J3Nob3J0JyxsZXZlbD0nREEnKSAlPiUgcHJlcF9kYXRhCmRhdGFfZGI9Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLCByZWdpb25zPXJlZ2lvbnMsZ2VvX2Zvcm1hdD0nc3AnLGxhYmVscz0nc2hvcnQnLGxldmVsPSdEQicpCmBgYAoKd2hpY2ggd2UgdGhlbiByZS1hZ2dyZWdhdGUgdG8gbWFrZSBzdXJlIHdlIGRvbid0IG1pc3Mgb3ZlcmFsbCBjb3VudHMgZHVlIHRvIHByaXZhY3kgY3V0b2ZmcyBkaXN0cmlidXRlIHRoZW0KcHJvcG9ydGlvbmFsbHkgYW1vbmcgdGhlIHBvcHVsYXRpb24uCmBgYHtyfQpkYXRhX2N0QGRhdGEgPC0gZG90X2RlbnNpdHkucHJvcG9ydGlvbmFsX3JlX2FnZ3JlZ2F0ZShkYXRhPWRhdGFfY3RAZGF0YSxwYXJlbnRfZGF0YT1kYXRhX2NzZEBkYXRhLGdlb19tYXRjaD1zZXROYW1lcygiR2VvVUlEIiwiQ1NEX1VJRCIpLGNhdGVnb3JpZXM9Y2F0ZWdvcmllcyxiYXNlPSJQb3B1bGF0aW9uIikKZGF0YV9kYUBkYXRhIDwtIGRvdF9kZW5zaXR5LnByb3BvcnRpb25hbF9yZV9hZ2dyZWdhdGUoZGF0YT1kYXRhX2RhQGRhdGEscGFyZW50X2RhdGE9ZGF0YV9jdEBkYXRhLGdlb19tYXRjaD1zZXROYW1lcygiR2VvVUlEIiwiQ1RfVUlEIiksY2F0ZWdvcmllcz1jYXRlZ29yaWVzLGJhc2U9IlBvcHVsYXRpb24iKQpkYXRhX2RiQGRhdGEgPC0gZG90X2RlbnNpdHkucHJvcG9ydGlvbmFsX3JlX2FnZ3JlZ2F0ZShkYXRhPWRhdGFfZGJAZGF0YSxwYXJlbnRfZGF0YT1kYXRhX2RhQGRhdGEsZ2VvX21hdGNoPXNldE5hbWVzKCJHZW9VSUQiLCJEQV9VSUQiKSxjYXRlZ29yaWVzPWNhdGVnb3JpZXMsYmFzZT0iUG9wdWxhdGlvbiIpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmJnX2NvbG9yPSIjMTExMTExIgpiYXNlX2NvbG9yPSIjMzMzMzMzIgp0ZXh0X2NvbG9yPSIjZWVlZWVlIgp0aGVtZV9vcHRzPC1saXN0KHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBiZ19jb2xvciwgY29sb3VyID0gTkEpLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPWJnX2NvbG9yLCBzaXplPTEsbGluZXR5cGU9InNvbGlkIixjb2xvcj10ZXh0X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT03MCxoanVzdCA9IDAuNSwgY29sb3I9dGV4dF9jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTUwLGhqdXN0ID0gMC41LCBjb2xvcj10ZXh0X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZT0yNSwgY29sb3I9dGV4dF9jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGNvbG9yPXRleHRfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGNvbG9yPXRleHRfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9YmdfY29sb3IsIHNpemU9MSxsaW5ldHlwZT0ic29saWQiLGNvbG9yPWJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGZpbGwgPSBiZ19jb2xvcixjb2xvciA9IGJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgzLCAnbGluZXMnKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikpCgpiYXNlbWFwIDwtICAgZ2dwbG90KGRhdGFfY3NkKSArCiAgICBnZW9tX3BvbHlnb24oYWVzKGxvbmcsIGxhdCwgZ3JvdXAgPSBncm91cCksIGZpbGwgPSBiYXNlX2NvbG9yLCBzaXplPTAuMSwgY29sb3IgPSAnZ3JleScpICsKICAgIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQobnJvdz0xLG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0xNSkpKSArCiAgICBjb29yZF9tYXAocHJvamVjdGlvbj0ibGFtYmVydCIsIGxhdDA9NDksIGxhdD00OS40KSArCiAgICB0aGVtZV9vcHRzCgpgYGAKCgojI01hcApBbGwgdGhhdCdzIGxlZnQgdG8gZG8gaXMgdG8gY292ZXJ0IG91ciByZS1hZ2dyZWdhdGVkIGJsb2NrLWxldmVsIGRhdGEgdG8gZG90cywgdXNpbmcgdGhlIGBkb3RfZGVuc2l0eS5jb21wdXRlX2RvdHNgCmZ1bmN0aW9uIGZyb20gdGhlIFtgZG90ZGVuc2l0eWAgcGFja2FnZV0oKSBhbmQgZmVlZCBpdCBpbnRvIHRoZSBgZG90X2RlbnNpdHkuZG90c19tYXBgIGZ1bmN0aW9uIHRvIGFkZCB0aGVtIHRvCm91ciBiYXNlbWFwLgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEzLCBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9VFJVRX0KIyAxIGRvdCA9IDUgaW1taWdyYW50cwpzY2FsZT0yNQoKdGlnaHRfeCA8LSBjKC0xMjMuMjg4NTc0MjE4NzUsLTEyMi45NjMxMDQyNDgwNDY4OCkKdGlnaHRfeSA8LSBjKDQ5LjE2MzI5Nzc2MDQzMTQ3LDQ5LjM0NzQ5MzUxNDAxOTg5NSkKd2lkZV94IDwtYygtMTIzLjI5LC0xMjIuNikKd2lkZV95IDwtIGMoNDkuMDIsNDkuMzUpCmRvdHMuZGIgPC0gZG90X2RlbnNpdHkuY29tcHV0ZV9kb3RzKGdlb19kYXRhID0gZGF0YV9kYiwgY2F0ZWdvcmllcyA9IGNhdGVnb3JpZXMsIHNjYWxlPXNjYWxlKQpiYXNlbWFwICsKICAjIHpvb20gaW4gYSBiaXQKICBjb29yZF9maXhlZCh4bGltPXdpZGVfeCwgeWxpbT13aWRlX3ksIHJhdGlvID0gMS9jb3MoNDkuMi8xODAqcGkpKSArCiMgc2hhZGUgdW5wb3B1bGF0ZWQgYmxvY2tzCiMgIGdlb21fcG9seWdvbihkYXRhPWRhdGFfZGJbZGF0YV9kYiRQb3B1bGF0aW9uPD01LF0sIAojICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxvbmcsIGxhdCwgZ3JvdXAgPSBncm91cCksIAojICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICIjMjIyMjIyIiwgc2l6ZT0wLjEsIAojICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiIzIyMjIyMiIpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykgKwogIGxhYnMoY29sb3IgPSAiIiwKICAgICAgICAgICAgICAgIHRpdGxlPSJWaXNpYmxlIE1pbm9yaXR5IDIwMTYiLAogICAgICAgICAgICAgICAgY2FwdGlvbj0iU291cmNlOiBTdGF0Q2FuIENlbnN1cyAyMDE2IHZpYSBjYW5jZW5zdXMgJiBDZW5zdXNNYXBwZXIuY2EiLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoIjEgZG90ID0gIixzY2FsZSwiIHBlb3BsZSIpKSArIAogIGRvdF9kZW5zaXR5LmRvdHNfbWFwKGRvdHM9ZG90cy5kYixhbHBoYT0wLjc1LHNpemU9MC4yNSkKCiMgc2F2ZSBpbWFnZSBmb3IgbGF0ZXIKZ2dzYXZlKCcuLi9pbWFnZXMvbWlub3JpdHkucG5nJyx3aWR0aD0yNixoZWlnaHQ9MjYpCmBgYApgYGB7cn0Kc2NhbGU9MTAKZG90cy5kYjIgPC0gZG90X2RlbnNpdHkuY29tcHV0ZV9kb3RzKGdlb19kYXRhID0gZGF0YV9kYiwgY2F0ZWdvcmllcyA9IGNhdGVnb3JpZXMsIHNjYWxlPXNjYWxlKQpiYXNlbWFwICsKICAjIHpvb20gaW4gYSBiaXQKICBjb29yZF9maXhlZCh4bGltPXRpZ2h0X3gsIHlsaW09dGlnaHRfeSwgcmF0aW8gPSAxL2Nvcyg0OS4yLzE4MCpwaSkpICsKIyBzaGFkZSB1bnBvcHVsYXRlZCBibG9ja3MKIyAgZ2VvbV9wb2x5Z29uKGRhdGE9ZGF0YV9kYltkYXRhX2RiJFBvcHVsYXRpb248PTUsXSwgCiMgICAgICAgICAgICAgICAgICAgICAgICBhZXMobG9uZywgbGF0LCBncm91cCA9IGdyb3VwKSwgCiMgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gIiMyMjIyMjIiLCBzaXplPTAuMSwgCiMgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICIjMjIyMjIyIikgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gY29sb3JzKSArCiAgbGFicyhjb2xvciA9ICIiLAogICAgICAgICAgICAgICAgdGl0bGU9IlZpc2libGUgTWlub3JpdHkgMjAxNiIsCiAgICAgICAgICAgICAgICBjYXB0aW9uPSJTb3VyY2U6IFN0YXRDYW4gQ2Vuc3VzIDIwMTYgdmlhIGNhbmNlbnN1cyAmIENlbnN1c01hcHBlci5jYSIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMCgiMSBkb3QgPSAiLHNjYWxlLCIgcGVvcGxlIikpICsgCiAgZG90X2RlbnNpdHkuZG90c19tYXAoZG90cz1kb3RzLmRiMixhbHBoYT0wLjc1LHNpemU9MC4yNSkKCiMgc2F2ZSBpbWFnZSBmb3IgbGF0ZXIKZ2dzYXZlKCcuLi9pbWFnZXMvbWlub3JpdHlfdmFuLnBuZycsd2lkdGg9MjYsaGVpZ2h0PTI2KQoKYGBgCgo=